#ifndef INCLUDE_FS_H_
#define INCLUDE_FS_H_

#include "type.h"

// ---------------
// | super block |
// ---------------
// |  inode map  |
// ---------------
// | sector map  |
// ---------------
// |    inode    |
// ---------------
// |    data     |
// ---------------

#define KFS_MAGIC (0x66666666)
#define OFFSET_256M (0X10000000)
#define OFFSET_512M (0x20000000)
#define OFFSET_1G (0x40000000)
#define OFFSET_2G (0x80000000)
#define OFFSET_3G (0xC0000000)

#define FS_SIZE (0x20000000) // 512MB
//#define FS_SIZE (0x10000000)
#define NUM_IMAP_SECTOR 1
#define SECTOR_SIZE 512
#define BLOCK_SIZE 4096 // 4KB
#define NUM_FD 15
#define INODES_PERBLK 64 // inode:64B
#define INDIR_PERBLK  1024 // 4KB/4B

#define ENTRY_SECTOR 8

#define TYPE_FILE 1
#define TYPE_DIR 2
#define TYPE_ME 3
#define TYPE_FA 4

#define O_RDONLY 1
#define O_WRONLY 2
#define O_RDWR 3

#define FS_SD_BASE 0X10000000
#define VTP(x) ((uint32_t)((uint64_t)x) & 0x1fffffff)
#define DIR_BLK_NUM 10
#define MAX_NAME_LEN 16
#define PATH_NUM 4
#define ALIGN_BY_SECTOR 0xfffffffffffffe00

#define SUPER_BLOCK_SIZE 0x200
#define IMAP_SZ 0x200              // 0x200*8 = 0x1000 inodes
#define INODES_SZ 0x40000          // 0x1000*64 = 0x40000B
#define BMAP_SZ 0x4000             // 0x4000*8 = 0x20000 blocks
#define DATA_BLOCK_SIZE 0x20000000 // 0x20000*4096B = 512MB

#define SUPER_BLK_ADDR 0xffffffffa0f00200
#define BMAP_ADDR 0xffffffffa0f00400
#define IMAP_ADDR 0xffffffffa0f04400
#define INODE_ADDR 0xffffffffa0f04600
#define DATA_BLK_ADDR 0xffffffffa0f44600

// File System: OFFSET_512M
#define SUPER_BLK_SD_ADDR 0x20000000
#define BMAP_SD_ADDR 0x20000200
#define IMAP_SD_ADDR 0x20004200
#define INODE_SD_ADDR 0x20004400
#define DATA_BLK_SD_ADDR 0x20044400

// offset is sector offset
typedef struct super_block
{
    uint32_t magic_num;
    uint32_t fs_sz;        //file system size
    uint32_t start_sector; // start sector in SD card

    uint64_t blkmap_addr; // virtual address in memory
    uint64_t imap_addr;
    uint64_t inodes_addr;
    uint64_t datablock_addr;

    uint32_t blkmap_off; // offset in disk
    uint32_t imap_off;
    uint32_t inodes_off;
    uint32_t datablock_off;

    uint32_t inode_total_num;
    uint32_t inode_free_num;

    uint32_t datablk_total_num;
    uint32_t datablk_free_num;

    uint32_t ino_alloc;
    uint32_t blk_alloc;

} super_block_t;

typedef struct inode_entry
{
    uint32_t ino;
    uint8_t mode;   // r/ w/ rw
    uint32_t used_sz;
    uint32_t num;                 //used num of data-block (for file) or dentry (for entry)
    uint32_t direct[DIR_BLK_NUM]; //inode->data
} inode_entry_t; // 4*16 = 64B

typedef struct dir_entry
{
    char name[MAX_NAME_LEN];
    uint32_t type; //0: NONE, 1: file, 2: dentry, 3:".", 4:".."
    uint32_t ino;

} dir_entry_t;

typedef struct fd
{
    uint32_t ino;
    uint32_t access;
    uint32_t r_cur_pos;
    uint32_t w_cur_pos;

} fd_t;


super_block_t *superblk;
uint32_t *superblk_buf;
uint8_t *blkmap;
uint8_t *inodemap;
uint32_t *inode_buf;
inode_entry_t *inode;
inode_entry_t *cur_inode;
int dir_cur_dep;
char path[PATH_NUM][MAX_NAME_LEN];

void do_init_fs(void);
void do_statfs(void);
int do_mkfs(void);
int do_cd(char *dir);
int do_ls(void);

void write_inode(uint32_t ino);
uint32_t alloc_datablk(void);
void free_inode(int ino);
int alloc_fd(uint32_t ino, uint32_t access);
uint32_t alloc_inode(void);
void CLEAR_BMAP(uint32_t sd_addr);
void write_block(void *memory_addr, uint32_t sd_addr);
void init_entry(uint32_t sd_addr, uint32_t used_ino, uint32_t par_ino);
void split(int *argc, char argv[][MAX_NAME_LEN], char *cmd);
void print(inode_entry_t *cur_dir);



void read_super_block(void);
// int readdir(char *name);
int do_mkdir(char *name);
int do_rmdir(char *name);
// int mknod(char *name);
int do_touch(char *name);

int do_open(char *name, uint32_t access);
int do_write(uint32_t fd, char *buff, uint32_t size);
int do_read(uint32_t fd, char *buff, uint32_t size);
int do_close(uint32_t fd);
int do_cat(char *name);

#endif
